home *** CD-ROM | disk | FTP | other *** search
/ Skunkware 5 / Skunkware 5.iso / src / Tools / lynx-2.4 / WWW / Library / Implementation / HTAccess.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-06-28  |  30.2 KB  |  1,117 lines

  1. /*        Access Manager                    HTAccess.c
  2. **        ==============
  3. **
  4. ** Authors
  5. **    TBL    Tim Berners-Lee timbl@info.cern.ch
  6. **    JFG    Jean-Francois Groff jfg@dxcern.cern.ch
  7. **    DD    Denis DeLaRoca (310) 825-4580  <CSP1DWD@mvs.oac.ucla.edu>
  8. **    FM    Foteos Macrides macrides@sci.wfeb.edu
  9. **      PDM     Danny Mayer mayer@ljo.dec.com
  10. **
  11. ** History
  12. **       8 Jun 92 Telnet hopping prohibited as telnet is not secure TBL
  13. **    26 Jun 92 When over DECnet, suppressed FTP, Gopher and News. JFG
  14. **     6 Oct 92 Moved HTClientHost and logfile into here. TBL
  15. **    17 Dec 92 Tn3270 added, bug fix. DD
  16. **     4 Feb 93 Access registration, Search escapes bad chars TBL
  17. **          PARAMETERS TO HTSEARCH AND HTLOADRELATIVE CHANGED
  18. **    28 May 93 WAIS gateway explicit if no WAIS library linked in.
  19. **    31 May 94 Added DIRECT_WAIS support for VMS. FM
  20. **      27 Jan 95 Fixed proxy support to use NNTPSERVER for checking
  21. **                whether or not to use the proxy server. PDM
  22. **      27 Jan 95 Ensured that proxy service will be overridden for files
  23. **          on the local host (because HTLoadFile() doesn't try ftp
  24. **          for those) and will substitute ftp for remote files. FM
  25. **      28 Jan 95 Tweeked PDM's proxy override mods to handle port info
  26. **          for news and wais URL's. FM
  27. **
  28. ** Bugs
  29. **    This module assumes that that the graphic object is hypertext, as it
  30. **    needs to select it when it has been loaded.  A superclass needs to be
  31. **    defined which accepts select and select_anchor.
  32. */
  33.  
  34. #ifdef VMS
  35. #define DIRECT_WAIS
  36. #endif /* VMS */
  37.  
  38. #ifndef DEFAULT_WAIS_GATEWAY
  39. #define DEFAULT_WAIS_GATEWAY "http://www.w3.org:8001"
  40. #endif
  41.  
  42. #include "HTUtils.h"
  43. #include "HTAlert.h"
  44. /* Implements:
  45. */
  46. #include "HTAccess.h"
  47.  
  48. /* Uses:
  49. */
  50.  
  51. #include "HTParse.h"
  52. #include "HTML.h"        /* SCW */
  53.  
  54. #ifndef NO_RULES
  55. #include "HTRules.h"
  56. #endif
  57.  
  58. /*#include <stdio.h> included by HTUtils.h -- FM */
  59.  
  60. #include "HTList.h"
  61. #include "HText.h"    /* See bugs above */
  62. #include "HTAlert.h"
  63.  
  64. #include "LYexit.h"
  65. #include "LYLeaks.h"
  66.  
  67. /*    These flags may be set to modify the operation of this module
  68. */
  69. PUBLIC char * HTClientHost = 0; /* Name of remote login host if any */
  70. PUBLIC FILE * logfile = 0;    /* File to which to output one-liners */
  71. PUBLIC BOOL HTSecure = NO;    /* Disable access for telnet users? */
  72.  
  73. PUBLIC BOOL using_proxy = NO; /* are we using a proxy gateway? */
  74. /*    To generate other things, play with these:
  75. */
  76.  
  77. PUBLIC HTFormat HTOutputFormat = NULL;
  78. PUBLIC HTStream* HTOutputStream = NULL;    /* For non-interactive, set this */ 
  79.  
  80. PRIVATE HTList * protocols = NULL;   /* List of registered protocol descriptors */
  81.  
  82. PUBLIC char *use_this_url_instead = NULL;
  83.  
  84.  
  85. /*    Register a Protocol                HTRegisterProtocol
  86. **    -------------------
  87. */
  88.  
  89. PUBLIC BOOL HTRegisterProtocol(protocol)
  90.     HTProtocol * protocol;
  91. {
  92.     if (!protocols) protocols = HTList_new();
  93.     HTList_addObject(protocols, protocol);
  94.     return YES;
  95. }
  96.  
  97.  
  98. /*    Register all known protocols
  99. **    ----------------------------
  100. **
  101. **    Add to or subtract from this list if you add or remove protocol modules.
  102. **    This routine is called the first time the protocol list is needed,
  103. **    unless any protocols are already registered, in which case it is not called.
  104. **    Therefore the application can override this list.
  105. **
  106. **    Compiling with NO_INIT prevents all known protocols from being forced
  107. **    in at link time.
  108. */
  109. #ifndef NO_INIT
  110. PRIVATE void HTAccessInit NOARGS            /* Call me once */
  111. {
  112. GLOBALREF HTProtocol HTTP, HTTPS, HTFile, HTTelnet, HTTn3270, HTRlogin;
  113. #ifndef DECNET
  114. GLOBALREF  HTProtocol HTFTP, HTNews, HTGopher;
  115. #ifdef DIRECT_WAIS
  116. GLOBALREF  HTProtocol HTWAIS;
  117. #endif
  118.     HTRegisterProtocol(&HTFTP);
  119.     HTRegisterProtocol(&HTNews);
  120.     HTRegisterProtocol(&HTGopher);
  121. #ifdef DIRECT_WAIS
  122.     HTRegisterProtocol(&HTWAIS);
  123. #endif
  124. #endif
  125.  
  126.     /* add this at some point in the future
  127.      */
  128.     /* HTRegisterProtocol(&HTTPS); */
  129.     HTRegisterProtocol(&HTTP);
  130.     HTRegisterProtocol(&HTFile);
  131.     HTRegisterProtocol(&HTTelnet);
  132.     HTRegisterProtocol(&HTTn3270);
  133.     HTRegisterProtocol(&HTRlogin);
  134. }
  135. #endif
  136.  
  137.  /*                                                  override_proxy()
  138.  **
  139.  **  Check the no_proxy environment variable to get the list
  140.  **  of hosts for which proxy server is not consulted.
  141.  **
  142.  **  no_proxy is a comma- or space-separated list of machine
  143.  **  or domain names, with optional :port part.  If no :port
  144.  **  part is present, it applies to all ports on that domain.
  145.  **
  146.  **  Example:
  147.  **          no_proxy="cern.ch,some.domain:8001"
  148.  **
  149.  */
  150.  PRIVATE BOOL override_proxy ARGS1(CONST char *, addr)
  151. {
  152.     CONST char * no_proxy = getenv("no_proxy");
  153.     char * p = NULL;
  154.     char * host = NULL;
  155.     char * access = NULL;
  156.     int port = 0;
  157.     int h_len = 0;
  158.  
  159.     /*
  160.      * Never proxy file:// URLs if they are on the local host.
  161.      * HTLoadFile() will not attempt ftp for those if direct
  162.      * access fails.  We'll check that first, in case no_proxy
  163.      * hasn't been defined. - FM
  164.      */
  165.     if (!addr || !(host = HTParse(addr, "", PARSE_HOST)))
  166.         return NO;
  167.  
  168.     if (!*host) {
  169.         free(host);
  170.     return NO;
  171.     }
  172.  
  173.     if((access = HTParse(addr, "", PARSE_ACCESS))) {
  174.         if (0==strcmp("file", access) &&
  175.         (0==strcmp(host, "localhost") ||
  176. #ifdef VMS
  177.              0==strcasecomp(host, HTHostName())))
  178. #else
  179.              0==strcmp(host, HTHostName())))
  180. #endif /* VMS */
  181.         {
  182.         free(host);
  183.         free(access);
  184.         return YES;
  185.     }
  186.     free(access);
  187.     access = NULL;
  188.     }
  189.  
  190.     if (!no_proxy)
  191.         return NO;
  192.  
  193.     if (p = strchr(host, ':')) {    /* Port specified */
  194.         *p++ = 0;                   /* Chop off port */
  195.         port = atoi(p);
  196.     }
  197.     else {                          /* Use default port */
  198.         access = HTParse(addr, "", PARSE_ACCESS);
  199.         if (access) {
  200.             if      (!strcmp(access,"http"))    port = 80;
  201.             else if (!strcmp(access,"ftp"))     port = 21;
  202.             else if (!strcmp(access,"gopher"))  port = 70;
  203.         else if (!strcmp(access,"news"))    port = 119;
  204.         else if (!strcmp(access,"wais"))    port = 210;
  205.             free(access);
  206.         }
  207.     }
  208.     if (!port)
  209.         port = 80;                  /* Default */
  210.     h_len = strlen(host);
  211.  
  212.     while (*no_proxy) {
  213.         CONST char * end;
  214.         CONST char * colon = NULL;
  215.         int templ_port = 0;
  216.         int t_len;
  217.  
  218.         while (*no_proxy && (WHITE(*no_proxy) || *no_proxy==','))
  219.             no_proxy++;             /* Skip whitespace and separators */
  220.  
  221.         end = no_proxy;
  222.         while (*end && !WHITE(*end) && *end != ',') {  /* Find separator */
  223.             if (*end==':') colon = end;                /* Port number given */
  224.             end++;
  225.         }
  226.  
  227.         if (colon) {
  228.             templ_port = atoi(colon+1);
  229.             t_len = colon - no_proxy;
  230.         }
  231.         else {
  232.             t_len = end - no_proxy;
  233.         }
  234.  
  235.         if ((!templ_port || templ_port == port)  &&
  236.             (t_len > 0  &&  t_len <= h_len  &&
  237.              !strncmp(host + h_len - t_len, no_proxy, t_len))) {
  238.             free(host);
  239.             return YES;
  240.         }
  241.         if (*end)
  242.         no_proxy = end+1;
  243.         else
  244.         break;
  245.     }
  246.  
  247.     free(host);
  248.     return NO;
  249. }
  250.  
  251. /*        Find physical name and access protocol
  252. **        --------------------------------------
  253. **
  254. **
  255. ** On entry,
  256. **    addr        must point to the fully qualified hypertext reference.
  257. **    anchor        a pareent anchor with whose address is addr
  258. **
  259. ** On exit,
  260. **    returns        HT_NO_ACCESS        Error has occured.
  261. **            HT_OK            Success
  262. **
  263. */
  264. PRIVATE int get_physical ARGS2(
  265.     CONST char *,        addr,
  266.     HTParentAnchor *,    anchor)
  267. {
  268.     char * access=NULL;        /* Name of access method */
  269.     char * physical=NULL;
  270.     char * Server_addr=NULL;
  271.  
  272. #ifndef NO_RULES
  273.     physical = HTTranslate(addr);
  274.     if (!physical) {
  275.     return HT_FORBIDDEN;
  276.     }
  277.     if (anchor->isISMAPScript == TRUE) {
  278.         StrAllocCat(physical, "?0,0");
  279.     if(TRACE)
  280.         fprintf(stderr,"HTAccess: Appending '?0,0' coordinate pair.\n");
  281.     }
  282.     HTAnchor_setPhysical(anchor, physical);
  283.     free(physical);            /* free our copy */
  284. #else
  285.     if (anchor->isISMAPScript == TRUE) {
  286.         StrAllocCopy(physical, addr);
  287.     StrAllocCat(physical, "?0,0");
  288.     if(TRACE)
  289.         fprintf(stderr,"HTAccess: Appending '?0,0' coordinate pair.\n");
  290.     HTAnchor_setPhysical(anchor, physical);
  291.     free(physical);            /* free our copy */
  292.     } else {
  293.         HTAnchor_setPhysical(anchor, addr);
  294.     }
  295. #endif
  296.  
  297.     access =  HTParse(HTAnchor_physical(anchor),
  298.             "file:", PARSE_ACCESS);
  299.  
  300. /*    Check whether gateway access has been set up for this
  301. **
  302. **    This function can be replaced by the rule system above.
  303. */
  304. #define USE_GATEWAYS
  305. #ifdef USE_GATEWAYS
  306.     /*
  307.      *    Make sure the using_proxy variable is false.
  308.      */
  309.     using_proxy = NO;
  310.  
  311.     /*
  312.      *  News is different, so we need to check the name of the server,
  313.      *  as well as the default port for selective exclusions.
  314.      */
  315.     if (strcasecomp(access, "news") == 0) {
  316.         if (getenv("NNTPSERVER")) {
  317.             StrAllocCopy(Server_addr, "news://");
  318.             StrAllocCat(Server_addr, (char *)getenv("NNTPSERVER"));
  319.             StrAllocCat(Server_addr, ":119/");
  320.          }
  321.     }
  322.     /*
  323.      * Wais also needs checking of the default port for selective exclusions.
  324.      */
  325.     else if (strcasecomp(access, "wais") == 0) {
  326.     char *host = NULL;
  327.     if ((host = HTParse(addr, "", PARSE_HOST))) {
  328.         if (!(strchr(host, ':'))) {
  329.             StrAllocCopy(Server_addr, "wais://");
  330.             StrAllocCat(Server_addr, host);
  331.         StrAllocCat(Server_addr, ":210/");
  332.         }
  333.         free(host);
  334.     }
  335.     else
  336.             StrAllocCopy(Server_addr, addr);
  337.     }
  338.     else {
  339.         StrAllocCopy(Server_addr, addr);
  340.     }
  341.  
  342.     if(!override_proxy(Server_addr)) {
  343.     char * gateway_parameter, *gateway, *proxy;
  344.  
  345.     /* search for gateways */
  346.     gateway_parameter = (char *)malloc(strlen(access)+20);
  347.     if (gateway_parameter == NULL) outofmem(__FILE__, "HTLoad");
  348.     strcpy(gateway_parameter, "WWW_");
  349.     strcat(gateway_parameter, access);
  350.     strcat(gateway_parameter, "_GATEWAY");
  351.     gateway = (char *)getenv(gateway_parameter); /* coerce for decstation */
  352.  
  353.     /* search for proxy servers */
  354.     /*
  355.      * If we got to here, a file URL is for ftp on a remote host. - FM
  356.      */
  357.     if (0==strcmp(access, "file"))
  358.         strcpy(gateway_parameter, "ftp");
  359.     else
  360.         strcpy(gateway_parameter, access);
  361.         strcat(gateway_parameter, "_proxy");
  362.     proxy = (char *)getenv(gateway_parameter);
  363.     free(gateway_parameter);
  364.  
  365.     if(TRACE && gateway)
  366.         fprintf(stderr,"Gateway found: %s\n",gateway);
  367.     if(TRACE && proxy)
  368.         fprintf(stderr,"proxy server found: %s\n",proxy);
  369.     
  370. #ifndef DIRECT_WAIS
  371.     if (!gateway && 0==strcmp(access, "wais")) {
  372.         gateway = DEFAULT_WAIS_GATEWAY;
  373.     }
  374. #endif /* direct wais */
  375.  
  376.     /* proxy servers have precedence over gateway servers */
  377.     if(proxy) {
  378.         char * gatewayed=0;
  379.             StrAllocCopy(gatewayed,proxy);
  380.         /*
  381.          * Ensure that the proxy server uses ftp for file URLs. - FM
  382.          */
  383.         if (0==strncmp(addr, "file", 4)) {
  384.                 StrAllocCat(gatewayed, "ftp");
  385.                 StrAllocCat(gatewayed, addr+4);
  386.         } else
  387.                 StrAllocCat(gatewayed, addr);
  388.             using_proxy = YES;
  389.             HTAnchor_setPhysical(anchor, gatewayed);
  390.         free(gatewayed);
  391.         free(access);
  392.  
  393.             access =  HTParse(HTAnchor_physical(anchor),
  394.             "http:", PARSE_ACCESS);
  395.  
  396.     } else if (gateway) {
  397.         char * path = HTParse(addr, "",
  398.             PARSE_HOST + PARSE_PATH + PARSE_PUNCTUATION);
  399.         /* Chop leading / off to make host into part of path */
  400.         char * gatewayed = HTParse(path+1, gateway, PARSE_ALL);
  401.         free(path);
  402.             HTAnchor_setPhysical(anchor, gatewayed);
  403.         free(gatewayed);
  404.         free(access);
  405.         
  406.             access =  HTParse(HTAnchor_physical(anchor),
  407.             "http:", PARSE_ACCESS);
  408.     } 
  409.     }
  410.     free(Server_addr);
  411. #endif /* use gateways */
  412.  
  413.  
  414. /*    Search registered protocols to find suitable one
  415. */
  416.     {
  417.     int i, n;
  418. #ifndef NO_INIT
  419.         if (!protocols) HTAccessInit();
  420. #endif
  421.     n = HTList_count(protocols);
  422.     for (i=0; i<n; i++) {
  423.         HTProtocol *p = HTList_objectAt(protocols, i);
  424.         if (strcmp(p->name, access)==0) {
  425.         HTAnchor_setProtocol(anchor, p);
  426.         free(access);
  427.         return (HT_OK);
  428.         }
  429.     }
  430.     }
  431.  
  432.     free(access);
  433.     return HT_NO_ACCESS;
  434. }
  435.  
  436.  
  437. /*        Load a document
  438. **        ---------------
  439. **
  440. **    This is an internal routine, which has an address AND a matching
  441. **    anchor.  (The public routines are called with one OR the other.)
  442. **
  443. ** On entry,
  444. **    addr        must point to the fully qualified hypertext reference.
  445. **    anchor        a pareent anchor with whose address is addr
  446. **
  447. ** On exit,
  448. **    returns        <0        Error has occured.
  449. **            HT_LOADED    Success
  450. **            HT_NO_DATA    Success, but no document loaded.
  451. **                    (telnet sesssion started etc)
  452. **
  453. */
  454. PRIVATE int HTLoad ARGS4(
  455.     CONST char *,        addr,
  456.     HTParentAnchor *,    anchor,
  457.     HTFormat,        format_out,
  458.     HTStream *,        sink)
  459. {
  460.     HTProtocol* p;
  461.     int status = get_physical(addr, anchor);
  462.     if (status == HT_FORBIDDEN) {
  463.         return HTLoadError(sink, 500, "Access forbidden by rule");
  464.     }
  465.     if (status < 0) return status;    /* Can't resolve or forbidden */
  466.     
  467.     p = HTAnchor_protocol(anchor);
  468.     anchor->underway = TRUE;            /* Hack to deal with caching */
  469.     status= (*(p->load))(HTAnchor_physical(anchor),
  470.                 anchor, format_out, sink);
  471.     anchor->underway = FALSE;
  472.     return status;
  473. }
  474.  
  475. /*        Get a save stream for a document
  476. **        --------------------------------
  477. */
  478. PUBLIC HTStream *HTSaveStream ARGS1(HTParentAnchor *, anchor)
  479. {
  480.     HTProtocol * p = HTAnchor_protocol(anchor);
  481.     if (!p) return NULL;
  482.     
  483.     return (*p->saveStream)(anchor);
  484.     
  485. }
  486.  
  487.  
  488. /*        Load a document - with logging etc
  489. **        ----------------------------------
  490. **
  491. **    - Checks or documents already loaded
  492. **    - Logs the access
  493. **    - Allows stdin filter option
  494. **    - Trace ouput and error messages
  495. **
  496. **    On Entry,
  497. **      anchor        is the node_anchor for the document
  498. **        full_address      The address of the document to be accessed.
  499. **        filter            if YES, treat stdin as HTML
  500. **
  501. **    On Exit,
  502. **        returns    YES     Success in opening document
  503. **                   NO      Failure 
  504. **
  505. */
  506.  
  507. PRIVATE BOOL HTLoadDocument ARGS4(
  508.     CONST char *,        full_address,
  509.     HTParentAnchor *,    anchor,
  510.     HTFormat,        format_out,
  511.     HTStream*,        sink)
  512.  
  513. {
  514.     int            status;
  515.     HText *    text;
  516.     char * address_to_load =  (char *)full_address;
  517.     extern char LYforce_no_cache;  /* from GridText.c */
  518.     extern char *redirecting_url;
  519.     char *cp;
  520.     char ForcingNoCache = LYforce_no_cache;
  521.     static int redirection_attempts = 0;
  522.  
  523. #ifdef DIRED_SUPPORT
  524.     extern BOOLEAN lynx_edit_mode;
  525. #endif
  526.  
  527.     if (TRACE) fprintf (stderr,
  528.       "HTAccess: loading document %s\n", address_to_load);
  529.  
  530.     /*
  531.      * Free use_this_url_instead if not done elsewhere. - FM
  532.      */
  533.     if (use_this_url_instead)
  534.         free(use_this_url_instead);
  535.     use_this_url_instead = NULL;
  536.  
  537.     /*
  538.      * Make sure some yoyo doesn't send us 'round in circles
  539.      * with redirecting URLs that point back to themselves.
  540.      * We'll set an arbitrary limit of 10 redirections per
  541.      * requested URL from a user.  - FM
  542.      */
  543.     if (redirection_attempts > 10) {
  544.         redirection_attempts = 0;
  545.     HTAlert("Redirection limit of 10 URL's reached.");
  546.         return NO;
  547.     }
  548.  
  549.     /*
  550.      * Check whether this is a redirecting URL, and keep re-checking
  551.      * until we get to the final destination or redirection limit. - FM
  552.      */
  553.     while ((cp=HTAnchor_physical(anchor)) && 0==strncmp(cp,"Location=",9)) {
  554.         DocAddress NewDoc;
  555.  
  556.         if (TRACE) {
  557.             fprintf (stderr, "HTAccess: '%s' is a redirection URL.\n",
  558.                               anchor->address);
  559.             fprintf (stderr, "HTAccess: Redirecting to '%s'\n", cp+9);
  560.         }
  561.  
  562.         /*
  563.      * Don't exceed the redirection_attempts limit. - FM
  564.      */
  565.     if (++redirection_attempts > 10) {
  566.         HTAlert("Redirection limit of 10 URL's reached.");
  567.             redirection_attempts = 0;
  568.         if(use_this_url_instead) {
  569.             free(use_this_url_instead);
  570.         use_this_url_instead = NULL;
  571.         }
  572.             return NO;
  573.     }
  574.  
  575.     /*
  576.      * Set up the redirection. - FM
  577.      */
  578.         StrAllocCopy(use_this_url_instead, cp+9);
  579.         NewDoc.address = use_this_url_instead;
  580.         NewDoc.post_data = 0;
  581.         NewDoc.post_content_type = 0;
  582.         anchor = (HTParentAnchor *)HTAnchor_findAddress(&NewDoc);
  583.     }
  584.     /*
  585.      * If we had redirection, go back and check out the URL. - FM
  586.      */
  587.     if (use_this_url_instead) {
  588.         if (redirecting_url) {
  589.         free(redirecting_url);
  590.         redirecting_url = NULL;
  591.     }
  592.         return(NO);
  593.     }
  594.  
  595.     /*
  596.      * See if we can use an already loaded document.
  597.      */
  598.     if (!LYforce_no_cache && (text=(HText *)HTAnchor_document(anchor))) {    
  599.     /* Already loaded */
  600.         if (TRACE) fprintf(stderr, "HTAccess: Document already in memory.\n");
  601.         HText_select(text);
  602.  
  603. #ifdef DIRED_SUPPORT
  604.     if (HTAnchor_format(anchor) == WWW_DIRED)
  605.        lynx_edit_mode = TRUE;
  606. #endif
  607.     return YES;
  608.     }
  609.  
  610.     /*
  611.      * Get the document from the net.
  612.      */    
  613.     LYforce_no_cache = NO;  /* reset after each time through */
  614.     
  615.     status = HTLoad(address_to_load, anchor, format_out, sink);
  616.  
  617.     if(TRACE)    {
  618.         fprintf(stderr, "HTAccess:  status=%d\n", status);
  619.     }
  620.     
  621.     /*
  622.      * Log the access if necessary
  623.      */
  624.     if (logfile) {
  625.     time_t theTime;
  626.     time(&theTime);
  627.     fprintf(logfile, "%24.24s %s %s %s\n",
  628.         ctime(&theTime),
  629.         HTClientHost ? HTClientHost : "local",
  630.         status<0 ? "FAIL" : "GET",
  631.         full_address);
  632.     fflush(logfile);    /* Actually update it on disk */
  633.     if (TRACE) fprintf(stderr, "Log: %24.24s %s %s %s\n",
  634.         ctime(&theTime),
  635.         HTClientHost ? HTClientHost : "local",
  636.         status<0 ? "FAIL" : "GET",
  637.         full_address);
  638.     }
  639.     
  640.     /*
  641.      * Check out what we received.
  642.      *
  643.      */
  644.     if (status == HT_REDIRECTING)
  645.       {
  646.         /* Exported from HTMIME.c, of all places. *//** NO!! - FM **/
  647.     /*
  648.      * Doing this via HTMIME.c meant that the redirection cover
  649.      * page was already loaded before we learned that we want a
  650.      * different URL.  Also, changing anchor->address, as Lynx
  651.      * was doing, meant we could never again access its hash
  652.      * table entry, creating an insolvable memory leak.  Instead,
  653.      * we'll load the new URL in anchor->physical, preceded by a
  654.      * token, which we can check to make replacements on subsequent
  655.      * access attempts.  We'll check recursively, and retrieve the
  656.      * final URL if we had multiple redirections to it.  If we just
  657.      * went to HTLoad now, as Lou originally had this, we couldn't do
  658.      * Lynx's security checks and alternate handling of some URL types.
  659.      * So, instead, we'll go all the way back to the top of getfile
  660.      * in LYGetFile.c when the status is HT_REDIRECTING.  This may
  661.      * seem bizarre, but it works like a charm! - FM
  662.      */
  663.         if (TRACE)
  664.           {
  665.             fprintf (stderr, "HTAccess: '%s' is a redirection URL.\n",
  666.                      address_to_load);
  667.             fprintf (stderr, "HTAccess: Redirecting to '%s'\n",
  668.                      redirecting_url);
  669.           }
  670.     /* prevent circular references 
  671.      */
  672.     if(strcmp(address_to_load, redirecting_url))
  673.       { /* if different */
  674.         /*
  675.          * Load token and redirecting url into anchor->physical
  676.          */
  677.         StrAllocCopy(anchor->physical, "Location=");
  678.         StrAllocCat(anchor->physical, redirecting_url);
  679.  
  680.             /*
  681.          * Set up flags before return to getfile.
  682.          */
  683.             StrAllocCopy(use_this_url_instead, redirecting_url);
  684.         if(ForcingNoCache)
  685.             LYforce_no_cache = YES;
  686.         redirection_attempts = 0;
  687.         free(redirecting_url);
  688.         redirecting_url = NULL;
  689.         return(NO);
  690.       }
  691.     redirection_attempts = 0;
  692.     free(redirecting_url);
  693.     redirecting_url = NULL;
  694.     return(YES);
  695.       }
  696.  
  697.     /*
  698.      * We did not receive a redirecting URL. - FM
  699.      */
  700.     redirection_attempts = 0;
  701.     if(redirecting_url)
  702.     free(redirecting_url);
  703.     redirecting_url = NULL;
  704.  
  705.     if (status == HT_LOADED) {
  706.     if (TRACE) {
  707.         fprintf(stderr, "HTAccess: `%s' has been accessed.\n",
  708.         full_address);
  709.     }
  710.     return YES;
  711.     }
  712.  
  713.     if (status == HT_NO_DATA) {
  714.     if (TRACE) {
  715.         fprintf(stderr, 
  716.         "HTAccess: `%s' has been accessed, No data left.\n",
  717.         full_address);
  718.     }
  719.     return NO;
  720.     }
  721.     
  722.     if (status == HT_NOT_LOADED) {
  723.     if (TRACE) {
  724.         fprintf(stderr, 
  725.         "HTAccess: `%s' has been accessed, No data loaded.\n",
  726.         full_address);
  727.     }
  728.     return NO;
  729.     }
  730.     
  731.     if (status == HT_INTERRUPTED) {
  732.     if (TRACE) {
  733.         fprintf(stderr, 
  734.         "HTAccess: `%s' has been accessed, transfer interrupted.\n",
  735.         full_address);
  736.     }
  737. /*    _HTProgress("Data transfer interrupted."); */
  738.     return NO;
  739.     }
  740.  
  741.     if (status<=0) {              /* Failure in accessing a document */
  742.     char *temp = NULL;
  743.     StrAllocCopy(temp, "Can't Access `");
  744.     StrAllocCat(temp, full_address);
  745.     StrAllocCat(temp, "'");
  746.     _HTProgress(temp);
  747.     free(temp);
  748.     if (TRACE) fprintf(stderr, 
  749.         "HTAccess: Can't access `%s'\n", full_address);
  750.     HTLoadError(sink, 500, "Unable to access document.");
  751.     return NO;
  752.     }
  753.  
  754.     /* If you get this, then please find which routine is returning
  755.        a positive unrecognised error code! */
  756.  
  757.     fprintf(stderr,
  758.     "**** HTAccess: socket or file number returned by obsolete load routine!\n");
  759.     fprintf(stderr,
  760.     "**** HTAccess: Internal software error. Please mail www-bug@www.w3.org!\n");
  761.     fprintf(stderr, "**** HTAccess: Status returned was: %d\n",status);
  762.     exit(-6996);
  763.  
  764. } /* HTLoadDocument */
  765.  
  766.  
  767.  
  768. /*        Load a document from absolute name
  769. **        ---------------
  770. **
  771. **    On Entry,
  772. **        addr     The absolute address of the document to be accessed.
  773. **        filter   if YES, treat document as HTML
  774. **
  775. **    On Exit,
  776. **        returns    YES     Success in opening document
  777. **                   NO      Failure 
  778. **
  779. **
  780. */
  781.  
  782. PUBLIC BOOL HTLoadAbsolute ARGS1(CONST DocAddress *,docaddr)
  783. {
  784.    return HTLoadDocument(docaddr->address,
  785.                HTAnchor_parent(HTAnchor_findAddress(docaddr)),
  786.                    HTOutputFormat ? HTOutputFormat : WWW_PRESENT,
  787.             HTOutputStream);
  788. }
  789.  
  790.  
  791. #ifdef NOT_USED_CODE
  792.  
  793. /*        Load a document from absolute name to stream
  794. **        --------------------------------------------
  795. **
  796. **    On Entry,
  797. **        addr     The absolute address of the document to be accessed.
  798. **        sink     if non-NULL, send data down this stream
  799. **
  800. **    On Exit,
  801. **        returns    YES     Success in opening document
  802. **                   NO      Failure 
  803. **
  804. **
  805. */
  806.  
  807. PUBLIC BOOL HTLoadToStream ARGS3(
  808.         CONST char *,    addr,
  809.         BOOL,         filter,
  810.         HTStream *,     sink)
  811. {
  812.    return HTLoadDocument(addr,
  813.                HTAnchor_parent(HTAnchor_findAddress(addr)),
  814.                    HTOutputFormat ? HTOutputFormat : WWW_PRESENT,
  815.             sink);
  816. }
  817.  
  818. #endif /* NOT_USED_CODE */
  819.  
  820.  
  821.  
  822. /*        Load a document from relative name
  823. **        ---------------
  824. **
  825. **    On Entry,
  826. **        relative_name     The relative address of the document
  827. **                  to be accessed.
  828. **
  829. **    On Exit,
  830. **        returns    YES     Success in opening document
  831. **                   NO      Failure 
  832. **
  833. **
  834. */
  835.  
  836. PUBLIC BOOL HTLoadRelative ARGS2(
  837.         CONST char *,        relative_name,
  838.         HTParentAnchor *,    here)
  839. {
  840.     DocAddress         full_address;
  841.     BOOL               result;
  842.     char *         mycopy = 0;
  843.     char *         stripped = 0;
  844.     char *        current_address =
  845.                     HTAnchor_address((HTAnchor*)here);
  846.  
  847.     full_address.address = 0;
  848.     full_address.post_data = 0;
  849.     full_address.post_content_type = 0;
  850.  
  851.     StrAllocCopy(mycopy, relative_name);
  852.  
  853.     stripped = HTStrip(mycopy);
  854.     full_address.address = HTParse(stripped,
  855.                current_address,
  856.            PARSE_ACCESS|PARSE_HOST|PARSE_PATH|PARSE_PUNCTUATION);
  857.     result = HTLoadAbsolute(&full_address);
  858.     /*
  859.     **  If we got redirection, result will be NO, but use_this_url_instead
  860.     **  will be set.  The calling routine should check both and do whatever
  861.     **  is appropriate. - FM
  862.     */
  863.     free(full_address.address);
  864.     free(current_address);
  865.     free(mycopy);  /* Memory leak fixed 10/7/92 -- JFG */
  866.     return result;
  867. }
  868.  
  869.  
  870. /*        Load if necessary, and select an anchor
  871. **        --------------------------------------
  872. **
  873. **    On Entry,
  874. **        destination              The child or parenet anchor to be loaded.
  875. **
  876. **    On Exit,
  877. **        returns    YES     Success
  878. **                   NO      Failure 
  879. **
  880. */
  881.  
  882. PUBLIC BOOL HTLoadAnchor ARGS1(HTAnchor *,destination)
  883. {
  884.     HTParentAnchor * parent;
  885.     BOOL loaded = NO;
  886.     if (!destination) return NO;    /* No link */
  887.     
  888.     parent  = HTAnchor_parent(destination);
  889.     
  890.     if (HTAnchor_document(parent) == NULL) {    /* If not alread loaded */
  891.                             /* TBL 921202 */
  892.  
  893.         BOOL result;
  894.         char * address = HTAnchor_address((HTAnchor*) parent);
  895.     result = HTLoadDocument(address, parent,
  896.         HTOutputFormat ? HTOutputFormat : WWW_PRESENT,
  897.         HTOutputStream);
  898.     free(address);
  899.     if (!result) return NO;
  900.     loaded = YES;
  901.     }
  902.     
  903.     {
  904.     HText *text = (HText*)HTAnchor_document(parent);
  905.     if (destination != (HTAnchor *)parent) {  /* If child anchor */
  906.         HText_selectAnchor(text, 
  907.             (HTChildAnchor*)destination); /* Double display? @@ */
  908.     } else {
  909.         if (!loaded) HText_select(text);
  910.     }
  911.     }
  912.     return YES;
  913.     
  914. } /* HTLoadAnchor */
  915.  
  916.  
  917. /*        Search
  918. **        ------
  919. **  Performs a keyword search on word given by the user. Adds the keyword to 
  920. **  the end of the current address and attempts to open the new address.
  921. **
  922. **  On Entry,
  923. **       *keywords      space-separated keyword list or similar search list
  924. **    here        is anchor search is to be done on.
  925. */
  926.  
  927. PRIVATE char hex(i)
  928.     int i;
  929. {
  930.     char * hexchars = "0123456789ABCDEF";
  931.     return hexchars[i];
  932. }
  933.  
  934. PUBLIC BOOL HTSearch ARGS2(
  935.     CONST char *,         keywords,
  936.     HTParentAnchor *,     here)
  937. {
  938.  
  939. #define acceptable \
  940. "1234567890abcdefghijlkmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.-_"
  941.  
  942.     char *q, *u;
  943.     CONST char * p, *s, *e;        /* Pointers into keywords */
  944.     char * address = 0;
  945.     BOOL result;
  946.     char * escaped = malloc(strlen(keywords)*3+1);
  947.     static CONST BOOL isAcceptable[96] =
  948.  
  949.     /*   0 1 2 3 4 5 6 7 8 9 A B C D E F */
  950.     {    0,0,0,0,0,0,0,0,0,0,1,0,0,1,1,0,    /* 2x   !"#$%&'()*+,-./     */
  951.          1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,    /* 3x  0123456789:;<=>?     */
  952.      1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,    /* 4x  @ABCDEFGHIJKLMNO  */
  953.      1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,1,    /* 5X  PQRSTUVWXYZ[\]^_     */
  954.      0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,    /* 6x  `abcdefghijklmno     */
  955.      1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0 };    /* 7X  pqrstuvwxyz{\}~    DEL */
  956.  
  957.     if (escaped == NULL) outofmem(__FILE__, "HTSearch");
  958.     
  959.     StrAllocCopy(address, here->isIndexAction);
  960.  
  961. /*    Convert spaces to + and hex escape unacceptable characters
  962. */
  963.     for(s=keywords; *s && WHITE(*s); s++) /*scan */ ;    /* Skip white space */
  964.     for(e = s + strlen(s); e>s && WHITE(*(e-1)) ; e--); /* Skip trailers */
  965.     for(q=escaped, p=s; p<e; p++) {            /* scan stripped field */
  966.         unsigned char c = (unsigned char)TOASCII(*p);
  967.         if (WHITE(*p)) {
  968.         *q++ = '+';
  969.     } else if (c>=32 && c<=(unsigned char)127 && isAcceptable[c-32]) {
  970.         *q++ = *p;                /* 930706 TBL for MVS bug */
  971.     } else {
  972.         *q++ = '%';
  973.         *q++ = hex((int)(c >> 4));
  974.         *q++ = hex((int)(c & 15));
  975.     }
  976.     } /* Loop over string */
  977.     
  978.     *q=0;
  979.                     /* terminate escaped sctring */
  980.     u=strchr(address, '?');        /* Find old search string */
  981.     if (u) *u = 0;                    /* Chop old search off */
  982.  
  983.     StrAllocCat(address, "?");
  984.     StrAllocCat(address, escaped);
  985.     free(escaped);
  986.     result = HTLoadRelative(address, here);
  987.     free(address);
  988.     
  989.     /*
  990.     **  If we got redirection, result will be NO, but use_this_url_instead
  991.     **  will be set.  The calling routine should check both and do whatever
  992.     **  is appropriate.  Only an http server (not a gopher or wais server)
  993.     **  could return redirection.  Lynx will go all the way back to its
  994.     **  mainloop() and subject a redirecting URL to all of its security and
  995.     **  restrictions checks. - FM
  996.     */
  997.     return result;
  998. }
  999.  
  1000.  
  1001. /*        Search Given Indexname
  1002. **        ------
  1003. **  Performs a keyword search on word given by the user. Adds the keyword to 
  1004. **  the end of the current address and attempts to open the new address.
  1005. **
  1006. **  On Entry,
  1007. **       *keywords      space-separated keyword list or similar search list
  1008. **    *addres        is name of object search is to be done on.
  1009. */
  1010.  
  1011. PUBLIC BOOL HTSearchAbsolute ARGS2(
  1012.     CONST char *,     keywords,
  1013.     CONST char *,     indexname)
  1014. {
  1015.     DocAddress abs_doc;
  1016.     HTParentAnchor * anchor;
  1017.     abs_doc.address = (char *)indexname;
  1018.     abs_doc.post_data = 0;
  1019.     abs_doc.post_content_type = 0;
  1020.  
  1021.     anchor = (HTParentAnchor*) HTAnchor_findAddress(&abs_doc);
  1022.     return HTSearch(keywords, anchor);
  1023. }
  1024.  
  1025. #ifdef NOT_USED_CODE
  1026. /*        Generate the anchor for the home page
  1027. **        -------------------------------------
  1028. **
  1029. **    As it involves file access, this should only be done once
  1030. **    when the program first runs.
  1031. **    This is a default algorithm -- browser don't HAVE to use this.
  1032. **    But consistency betwen browsers is STRONGLY recommended!
  1033. **
  1034. **    Priority order is:
  1035. **
  1036. **        1    WWW_HOME environment variable (logical name, etc)
  1037. **        2    ~/WWW/default.html
  1038. **        3    /usr/local/bin/default.html
  1039. **        4    http://www.w3.org/default.html
  1040. **
  1041. */
  1042. PUBLIC HTParentAnchor * HTHomeAnchor NOARGS
  1043. {
  1044.     char * my_home_document = NULL;
  1045.     char * home = (char *)getenv(LOGICAL_DEFAULT);
  1046.     char * ref;
  1047.     HTParentAnchor * anchor;
  1048.     
  1049.     if (home) {
  1050.         StrAllocCopy(my_home_document, home);
  1051.     
  1052. /*     Someone telnets in, they get a special home.
  1053. */
  1054. #define MAX_FILE_NAME 1024                    /* @@@ */
  1055.     } else  if (HTClientHost) {            /* Telnet server */
  1056.         FILE * fp = fopen(REMOTE_POINTER, "r");
  1057.     char * status;
  1058.     if (fp) {
  1059.         my_home_document = (char*) malloc(MAX_FILE_NAME);
  1060.         status = fgets(my_home_document, MAX_FILE_NAME, fp);
  1061.         if (!status) {
  1062.             free(my_home_document);
  1063.         my_home_document = NULL;
  1064.         }
  1065.         fclose(fp);
  1066.     }
  1067.     if (!my_home_document) StrAllocCopy(my_home_document, REMOTE_ADDRESS);
  1068.     }
  1069.  
  1070.     
  1071.  
  1072. #ifdef unix
  1073.  
  1074.     if (!my_home_document) {
  1075.     FILE * fp = NULL;
  1076.     CONST char * home =  (CONST char*)getenv("HOME");
  1077.     if (home) { 
  1078.         my_home_document = (char *)malloc(
  1079.         strlen(home)+1+ strlen(PERSONAL_DEFAULT)+1);
  1080.         if (my_home_document == NULL) outofmem(__FILE__, "HTLocalName");
  1081.         sprintf(my_home_document, "%s/%s", home, PERSONAL_DEFAULT);
  1082.         fp = fopen(my_home_document, "r");
  1083.     }
  1084.     
  1085.     if (!fp) {
  1086.         StrAllocCopy(my_home_document, LOCAL_DEFAULT_FILE);
  1087.         fp = fopen(my_home_document, "r");
  1088.     }
  1089.     if (fp) {
  1090.         fclose(fp);
  1091.     } else {
  1092.     if (TRACE) fprintf(stderr,
  1093.         "HTBrowse: No local home document ~/%s or %s\n",
  1094.         PERSONAL_DEFAULT, LOCAL_DEFAULT_FILE);
  1095.         free(my_home_document);
  1096.         my_home_document = NULL;
  1097.     }
  1098.     }
  1099. #endif
  1100.     ref = HTParse( my_home_document ?    my_home_document :
  1101.                 HTClientHost ? REMOTE_ADDRESS
  1102.                 : LAST_RESORT,
  1103.             "file:",
  1104.             PARSE_ACCESS|PARSE_HOST|PARSE_PATH|PARSE_PUNCTUATION);
  1105.     if (my_home_document) {
  1106.     if (TRACE) fprintf(stderr,
  1107.         "HTAccess: Using custom home page %s i.e. address %s\n",
  1108.         my_home_document, ref);
  1109.     free(my_home_document);
  1110.     }
  1111.     anchor = (HTParentAnchor*) HTAnchor_findAddress(ref);
  1112.     free(ref);
  1113.     return anchor;
  1114. }
  1115. #endif /* NOT_USED_CODE */
  1116.  
  1117.